using LitJson;
using GameCore.UI;
using System.Collections;
using System.Collections.Generic;
using Framework.Save;
using UnityEngine;

namespace GameCore
{
    public class SimpleAdventureRegion : Region
    {
        public AdventureUI ui;
        public AreaConfig entryArea;
        public Dictionary<string, AreaData> areas = new();
        public PlayerInventory playerInventory;
        public Character character;

        private void Start()
        {
            BuildAdventureTree();
            playerInventory.ResetData();
            ui.SetStamina(stamina);
            ui.ShowAreaSelectionPanel(true);
            SubscribeEvents();
        }

        private HashSet<EventSubscription> _subs = new();
        
        private void SubscribeEvents()
        {
            Debug.Log("SubscribeEvents");
            _subs.Add(LevelManager.SubscribeEvent<ItemPlacedEvent>(BackpackStaminaReward));
        }

        private void OnDestroy()
        {
            foreach (var sub in _subs) sub.Dispose();
            _subs.Clear();
        }
        
        #region Area Management

        public string currentAreaID;
        public AreaData CurrentArea => areas[currentAreaID];
        
        public AreaData GetArea(string areaID)
        {
            if (!areas.ContainsKey(areaID))
                areas[areaID] = new AreaData(areaID, this);
            return areas[areaID];                
        }

        private void BuildAdventureTree()
        {
            Queue<AreaConfig> queue = new();
            HashSet<AreaConfig> visited = new();
            queue.Enqueue(entryArea);
            while (queue.Count > 0)
            {
                var cfg = queue.Dequeue();
                if (visited.Contains(cfg)) continue;
                var area = GetArea(cfg.areaID);
                foreach (var connCfg in cfg.GetRandomConnections())
                {
                    queue.Enqueue(connCfg);
                    var connArea = GetArea(connCfg.areaID);
                    area.AddConnection(connArea);
                }
                visited.Add(cfg);
            }
            currentAreaID = entryArea.areaID.ToString();
            CurrentArea.accessState = AreaAccessState.Current;
            foreach (var area in areas.Values)
            {
                area.RecalculateDistanceIndex();
                area.UpdateAccessState();
            }
        }

        public bool AllowTransition(string areaID)
        {
            return CurrentArea.AllowTransition(areas[areaID]);
        }

        public void SetCurrentArea(string areaID)
        {
            CurrentArea.accessState = AreaAccessState.Visited;
            currentAreaID = areaID;
            CurrentArea.accessState = AreaAccessState.Current;
            foreach (var area in areas.Values) area.UpdateAccessState();
            areaDistance = CurrentArea.GetConfig().steps;
            position = 0;
            ui.SetTotalProgress(areaDistance);
            ui.ResetProgress();
            ui.AddSign(areaDistance);
            MoveForward();
        }
        
        #endregion

        #region Step Forward Management

        [Header("Exploration")]
        public int position = 0;
        public int areaDistance = 15;
        public int stamina = 25;
        public Transform[] itemSpawnPoints;
        public List<WorldItem> worldItems;
        public float transitionTime = 0.8f;
        public bool isMoving = false;

        public void MoveForward()
        {
            if (isMoving) return;
            if (stamina == 0) ForceEvacuate();
            if (position == areaDistance) ui.ShowAreaSelectionPanel(true);
            else StartCoroutine(_MoveForward());
        }

        private IEnumerator _MoveForward()
        {
            // Clear current items
            foreach (var item in worldItems)
            {
                if (item) Destroy(item.gameObject);
            }
            worldItems.Clear();
            isMoving = true;
            // Start and wait animation
            ui.SetProgress(position + 1, transitionTime);
            character.Move();
            yield return new WaitForSeconds(transitionTime);
            character.Stop();
            // Update data
            position += 1;
            stamina -= 1;
            ui.SetStamina(stamina);
            // Spawn new items or evacuate
            if (position == areaDistance && CurrentArea.GetConfig().canEvacuate)
            {
                ui.ShowEvacuateButton(true);
            }
            else
            {
                ui.ShowEvacuateButton(false);
                var (total, used) = playerInventory.CalculateGrids();
                var n = CurrentArea.GetConfig().itemCount;
                for (int i = 0; i < n; i++)
                    SpawnItem(itemSpawnPoints[i], total - used);
            }
            isMoving = false;
        }

        private void SpawnItem(Transform point, int rem)
        {
            bool isBackpack = false;
            float rand = Random.value;
            if (rem < 9) isBackpack = rand < 0.6f;
            else if (rem < 18) isBackpack = rand < 0.4f;
            else if (rem < 27) isBackpack = rand < 0.2f;
            else isBackpack = rand < 0.1f;
            var resID = isBackpack ? 
                CurrentArea.GetConfig().GetRandomBackpack().resourceID : 
                CurrentArea.GetConfig().GetRandomItem().resourceID;
            var item = PrefabFactory.CreateInstance<WorldItem>(resID);
            item.ResetData();
            item.transform.position = point.position;
            item.transform.parent = point;
            worldItems.Add(item);
        }
        
        private void BackpackStaminaReward(ItemPlacedEvent eventData)
        {
            Debug.Log($"Item {eventData.item.resourceID} placed in {eventData.inventory.resourceID}");
            if (eventData.inventory is not BackpackInventory backpack) return;
            if (!backpack.rewardGranted && backpack.GetData() is InventoryData inv && inv.IsFull())
            {
                stamina += backpack.staminaReward;
                ui.SetStamina(stamina);
            }
        }
        
        #endregion

        public void Evacuate()
        {
            LevelManager.EndAdventure(true);
        }

        public void ForceEvacuate()
        {
            Debug.Log("Force Evacuate");
            LevelManager.EndAdventure(false);
        }

        public override JsonData Serialize()
        {
            var json = JsonUtil.NewObject();
            json["regionID"] = resourceID.ToString();
            return json;
        }

        public override void Deserialize(JsonData json)
        {
            
        }
    }
}